package com.region.plugin;

import com.region.common.adapter.logging.LoggerAdapter;
import com.region.common.adapter.logging.LoggerAdapterFactory;
import com.region.common.utils.ClassUtils;
import com.region.plugin.proxy.ProxyHandler;
import com.region.plugin.proxy.cglib.CglibProxyHandler;
import com.region.plugin.proxy.jdk.JdkProxyHandler;

import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.ServiceLoader;

/**
 * Proxy Manager
 *
 * @author liujieyu
 * @date 2023/5/23 20:31
 * @desciption
 */
public abstract class AbstractProxyManager {

    private final LoggerAdapter loggerAdapter = LoggerAdapterFactory.getLogger(getClass());

    private Class<? extends ProxyHandler> type;

    private ProxyHandler proxyHandler;

    private List<Interceptor> interceptors = new LinkedList<>();

    protected AbstractProxyManager(boolean enableLoader) {
        if (enableLoader) {
            // Loading by using the Java SPI mechanism
            ServiceLoader<Interceptor> interceptorServiceLoader = ServiceLoader.load(Interceptor.class);
            for (Interceptor interceptor : interceptorServiceLoader) {
                addInterceptor(interceptor);
            }
        }
        init();
    }

    private void init() {
        ProxyTypeRegister.registerProxyHandler(new JdkProxyHandler());
        try {
            Class.forName("net.sf.cglib.proxy.Enhancer");
            ProxyTypeRegister.registerProxyHandler(new CglibProxyHandler());
        } catch (Exception e) {
            loggerAdapter.warn("The not found cglib dependency.");
        }
    }

    public void setProxyTarget(Class<? extends ProxyHandler> type) {
        this.type = type;
        this.proxyHandler = ProxyTypeRegister.getProxyHandler(type);
    }

    public void addInterceptor(Interceptor interceptor) {
        if (loggerAdapter.isDebugEnabled()) {
            loggerAdapter.debug("Adding " + interceptor + " interceptor, The number of interceptors currently owned is " + interceptors.size());
        }
        this.interceptors.add(interceptor);
        this.interceptors.sort(Comparator.comparingInt(Interceptor::order));
    }

    /**
     * Initialising the proxy approach
     *
     * @param target the target object
     */
    protected void autoProxyType(Object target) {
        if (this.type == null) {
            if (ClassUtils.getAllInterfaces(target.getClass()).length == 0) {
                this.proxyHandler = ProxyTypeRegister.getProxyHandler(CglibProxyHandler.class);
            } else {
                this.proxyHandler = ProxyTypeRegister.getProxyHandler(JdkProxyHandler.class);
            }
        }
        loggerAdapter.info("The current proxy uses " + this.type + ", The object to be represented is " + target);
    }

    /**
     * Creating a Proxy Object Using a Parameterless Constructor
     *
     * @param target
     * @return
     */
    public Object doInterceptor(Object target) {
        return doInterceptor(target, null);
    }

    /**
     * Load all interceptors and generate interceptor links and creating proxy objects with parametric constructors
     *
     * @param target the target object
     */
    public Object doInterceptor(Object target, Object[] args) {
        // auto the proxy approach
        autoProxyType(target);
        // The proxy object is generated and the link is generated
        Class<?>[] interfaces = ClassUtils.getAllInterfaces(target.getClass());
        for (Interceptor interceptor : interceptors) {
            target = proxyHandler.createProxyInstance(target, interfaces, interceptor, args);
        }
        // post complete processor
        proxyHandler.completeProxy(target, this.interceptors, args);
        return target;
    }

}
